'use strict';

/*!
 * =====================================================
 * 基类View，所有其他的业务View都是基于它的扩展。
 * 具备如下基本功能
 * render: 模板+数据 准备html，展示页面
 * events: 指定事件响应
 * template: 指定模板
 * request: 请求数据
 * =====================================================
 */

 (function() {
    var array = [],
        push = array.push,
        slice = array.slice,
        splice = array.splice;
 	
    var View = function(options) {
   		options || (options = {});
      _.extend(this, _.pick(options, viewOptions));
      this.setElement(_.result(this, 'el') || 'body');

   		this.initialize.apply(this, arguments);
   	}

    View.$ = $;

   	var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    var Events = {

        // Bind an event to a `callback` function. Passing `"all"` will bind
        // the callback to all events fired.
        on: function(name, callback, context) {
          if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
          this._events || (this._events = {});
          var events = this._events[name] || (this._events[name] = []);
          events.push({callback: callback, context: context, ctx: context || this});
          return this;
        },

        // Bind an event to only be triggered a single time. After the first time
        // the callback is invoked, it will be removed.
        once: function(name, callback, context) {
          if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
          var self = this;
          var once = _.once(function() {
            self.off(name, once);
            callback.apply(this, arguments);
          });
          once._callback = callback;
          return this.on(name, once, context);
        },

        // Remove one or many callbacks. If `context` is null, removes all
        // callbacks with that function. If `callback` is null, removes all
        // callbacks for the event. If `name` is null, removes all bound
        // callbacks for all events.
        off: function(name, callback, context) {
          var retain, ev, events, names, i, l, j, k;
          if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
          if (!name && !callback && !context) {
            this._events = void 0;
            return this;
          }
          names = name ? [name] : _.keys(this._events);
          for (i = 0, l = names.length; i < l; i++) {
            name = names[i];
            if (events = this._events[name]) {
              this._events[name] = retain = [];
              if (callback || context) {
                for (j = 0, k = events.length; j < k; j++) {
                  ev = events[j];
                  if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
                      (context && context !== ev.context)) {
                    retain.push(ev);
                  }
                }
              }
              if (!retain.length) delete this._events[name];
            }
          }

          return this;
        },

        // Trigger one or many events, firing all bound callbacks. Callbacks are
        // passed the same arguments as `trigger` is, apart from the event name
        // (unless you're listening on `"all"`, which will cause your callback to
        // receive the true name of the event as the first argument).
        trigger: function(name) {
          if (!this._events) return this;
          var args = slice.call(arguments, 1);
          if (!eventsApi(this, 'trigger', name, args)) return this;
          var events = this._events[name];
          var allEvents = this._events.all;
          if (events) triggerEvents(events, args);
          if (allEvents) triggerEvents(allEvents, arguments);
          return this;
        },

        // Tell this object to stop listening to either specific events ... or
        // to every object it's currently listening to.
        stopListening: function(obj, name, callback) {
          var listeningTo = this._listeningTo;
          if (!listeningTo) return this;
          var remove = !name && !callback;
          if (!callback && typeof name === 'object') callback = this;
          if (obj) (listeningTo = {})[obj._listenId] = obj;
          for (var id in listeningTo) {
            obj = listeningTo[id];
            obj.off(name, callback, this);
            if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
          }
          return this;
        }

    };

    // Regular expression used to split event strings.
    var eventSplitter = /\s+/;

    // Implement fancy features of the Events API such as multiple event
    // names `"change blur"` and jQuery-style event maps `{change: action}`
    // in terms of the existing API.
    var eventsApi = function(obj, action, name, rest) {
        if (!name) return true;

        // Handle event maps.
        if (typeof name === 'object') {
          for (var key in name) {
            obj[action].apply(obj, [key, name[key]].concat(rest));
          }
          return false;
        }

        // Handle space separated event names.
        if (eventSplitter.test(name)) {
          var names = name.split(eventSplitter);
          for (var i = 0, l = names.length; i < l; i++) {
            obj[action].apply(obj, [names[i]].concat(rest));
          }
          return false;
        }

        return true;
    };

    // A difficult-to-believe, but optimized internal dispatch function for
    // triggering events. Tries to keep the usual cases speedy (most internal
    // Backbone events have 3 arguments).
    var triggerEvents = function(events, args) {
        var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
        switch (args.length) {
          case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
          case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
          case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
          case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
          default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
        }
    };

    var viewOptions = ['el', 'events', 'appId', 'popup'];

    _.extend(View.prototype, Events, {
        $: function(selector) {
            return this.$el.find(selector);
        },
        setPopupElement: function() {
            var frag = document.createDocumentFragment(),
                wrapper = document.createElement('div'),
                list = document.createElement('ul');

            list.className = 'pop-ul pl0 clearfix no-margin w100';
            wrapper.className = 'popup top-pop';

            wrapper.appendChild(list);
            frag.appendChild(wrapper);

            this.popupEl = frag;
            this.popupLi = View.$(list);
        },
        initPopup: function(options) {
            options = options || {};

            this.setPopupElement();
            var item;
            for(var i = 0, l = options.length; i < l; i++) {
                item = options[i];
                this.popupLi.append('<li data-index="' + i + '" class="' + item.class + '"><img src="' + item.img + '"/><span>' + item.title + '</span></li>');
            }
            this.$el.append(this.popupEl);
            
            this.$el.on('click', '.popup-overlay', function(event) {
                this.hidePopup();
            }.bind(this));

            this.$el.on('click', '.top-pop li', function(event) {
                var index = View.$(this).data('index');
                options[index].action.call(this, event);
            });

            return this;
        },
        showPopup: function() {
            if(!this.popupEl) {
                return;
            }

            View.$.popup('.top-pop');
        },
        hidePopup: function() {
            if(!this.popupEl) {
                return;
            }

            View.$.closeModal('.top-pop');
        },
        setElement: function(el) {
            this.undelegateEvents();
            this.$el = el instanceof View.$ ? el : View.$(el);
            this.el = this.$el[0];
            this.delegateEvents();
            return this;
        },
        delegateEvents: function(events) {
            if (!(events || (events = _.result(this, 'events')))) return this;
            this.undelegateEvents();
            for (var key in events) {
                var method = events[key];
                if (!_.isFunction(method)) method = this[events[key]];
                if (!method) continue;
                var match = key.match(delegateEventSplitter);
                this.delegate(match[1], match[2], _.bind(method, this));
            }
            return this;
        },
        delegate: function(eventName, selector, listener) {
            this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
        },
        undelegateEvents: function() {
            if (this.$el) this.$el.off('.delegateEvents' + this.cid);
            return this;
        },
        initialize: function(){
            // SUI初始化
            $.init();
            FastClick.attach(this.el);

            if(this.appId) {
              this.initPopup(this.getServiceAction());

              this.on('favoriteStatus', this.handle.favoriteStatus);
              this.on('addFavorite', this.handle.addFavorite);
              this.on('cancelFavorite', this.handle.cancelFavorite);

              this.getFavoriteStatus();
            }
            
            return this;
        },
        render: function() {
            return this;
        },
        getUrlParams: function() {
          // 获取参数
          var search = location.search && location.search.split('?')[1],
              querys, params = {};

          // 获取appId参数
          querys = search.split('&');

          querys.forEach(function(item) {
              var splits = item.split('=');
              params[splits[0]] = decodeURI(splits[1]);
          })

          return params;
        },
        getFavoriteStatus: function() {
          API.Operation.getFavoriteStatus({APPID: this.appId}).execute().done(function(result) {
            this.trigger('favoriteStatus', result);
          }.bind(this));
        },
        addFavorite: function() {
          API.Operation.addFavorite({APPID: this.appId}).execute().done(function(result) {
            this.trigger('addFavorite', result);
          }.bind(this));
        },
        cancelFavorite: function() {
          API.Operation.cancelFavorite({APPID: this.appId}).execute().done(function(result) {
            this.trigger('cancelFavorite', result);
          }.bind(this));
        },
        handle: {
          favoriteStatus: function(result) {
            if (result.head.rtnCode === "000000") {
              this.favorite= result.body;
              this.toggleFavoriteIcon();
            }else {
              $.alert(result.head.rtnMsg);
            }
          },
          addFavorite: function(result) {
            if (result.head.rtnCode === "000000") {
              this.favorite = true;
              this.toggleFavoriteIcon();
            }else {
              $.alert(result.head.rtnMsg);
            }
          },
          cancelFavorite: function(result) {
            if (result.head.rtnCode === "000000") {
              this.favorite = false;
              this.toggleFavoriteIcon();
            }else {
              $.alert(result.head.rtnMsg);
            }
          }
        },
        toggleFavoriteIcon: function() {
          var icon = this.favorite ? '../../../images/shoucang-done.png' : '../../../images/shoucang-ic.png';
          var title = this.favorite ? '已收藏' : '收藏';

          this.popupLi.find('.fav').find('img').attr('src', icon).next().text(title);
        },
        getServiceAction: function() {
          return _.union([{
            title: '收藏',
            class: 'fav',
            img: '../../../images/shoucang-ic.png',
            action: function(event) {
              if(this.favorite) {
                // 取消收藏
                this.cancelFavorite();
              }else {
                // 添加收藏
                this.addFavorite();
              }
            }.bind(this)
          }], this.popup);
        }
    })

 	// Helper function to correctly set up the prototype chain, for subclasses.
    // Similar to `goog.inherits`, but uses a hash of prototype properties and
    // class properties to be extended.
    var extend = function(protoProps, staticProps) {
        var parent = this;
        var child;

        // The constructor function for the new subclass is either defined by you
        // (the "constructor" property in your `extend` definition), or defaulted
        // by us to simply call the parent's constructor.
        if (protoProps && _.has(protoProps, 'constructor')) {
            child = protoProps.constructor;
        } else {
            child = function(){ return parent.apply(this, arguments); };
        }

        // Add static properties to the constructor function, if supplied.
        _.extend(child, parent, staticProps);

        // Set the prototype chain to inherit from `parent`, without calling
        // `parent`'s constructor function.
        var Surrogate = function(){ this.constructor = child; };
        Surrogate.prototype = parent.prototype;
        child.prototype = new Surrogate;

        // Add prototype properties (instance properties) to the subclass,
        // if supplied.
        if (protoProps) _.extend(child.prototype, protoProps);

        // Set a convenience property in case the parent's prototype is needed
        // later.
        child.__super__ = parent.prototype;

        return child;
    };

    View.extend = extend;

 	window.View = View;
 })();